from datetime import datetime, timedelta
from typing import Any, Union, Optional
import logging

from jose import jwt
from passlib.context import CryptContext
from app.core.config import settings

# 配置日志
logger = logging.getLogger(__name__)

# 尝试使用bcrypt，如果不可用则回退到sha256_crypt
try:
    # 密码上下文，使用bcrypt算法进行密码哈希
    passwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
    # 测试bcrypt是否真正可用
    test_hash = passwd_context.hash("test")
    logger.info("使用bcrypt进行密码哈希")
except (ImportError, ValueError) as e:
    # 如果bcrypt不可用或出现后端错误，使用sha256_crypt作为替代
    logger.warning(f"bcrypt不可用: {str(e)}，使用sha256_crypt作为替代。建议安装bcrypt: pip install bcrypt")
    passwd_context = CryptContext(schemes=["sha256_crypt"], deprecated="auto")

# JWT密钥和算法设置
SECRET_KEY = settings.SECRET_KEY
ALGORITHM = "HS256"

def create_access_token(
    subject: Union[str, Any], expires_delta: Optional[timedelta] = None
) -> str:
    """创建JWT访问令牌
    
    根据提供的主题（通常是用户ID）和过期时间生成JWT令牌
    
    Args:
        subject: 令牌主题，通常是用户ID
        expires_delta: 令牌过期时间增量，如不提供则使用配置中的默认值
        
    Returns:
        str: 生成的JWT令牌字符串
    """
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
        )
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证密码
    
    比较明文密码和哈希密码是否匹配
    
    Args:
        plain_password: 明文密码
        hashed_password: 哈希后的密码
        
    Returns:
        bool: 密码是否匹配
    """
    return passwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    """获取密码哈希值
    
    将明文密码转换为哈希值
    
    Args:
        password: 明文密码
        
    Returns:
        str: 哈希后的密码
    """
    return passwd_context.hash(password)

def verify_token(token: str) -> Optional[str]:
    """验证JWT令牌
    
    解码并验证JWT令牌的有效性
    
    Args:
        token: JWT令牌字符串
        
    Returns:
        Optional[str]: 如果令牌有效，返回主题（通常是用户ID），否则返回None
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload.get("sub")
    except jwt.JWTError:
        return None